require "wondercap/lib/multi_configuration/files_reader"

module Wondercap
module Deployment

module OuterConfigurationMixin
  attr :area, true
  attr :snapshot, true
  attr :top_task, true
  
  # a hack actually - to get the top_task reference
  def self.included(base)
    base.send :alias_method, :trigger_without_outer_configuration_mixin, :trigger
    base.send :alias_method, :trigger, :trigger_with_outer_configuration_mixin
  end
  
  def trigger_with_outer_configuration_mixin(event, task = nil)
    if event == :start
      @top_task = task
    end
    trigger_without_outer_configuration_mixin(event, task)
  end

  def define_name_filter(name, callable)
    name_filters[name] = callable
  end
  
  def get_name_filter(name)
    raise NameError.new("no filter #{name} defined") unless name_filters.key? name.to_sym
    name_filters[name.to_sym]
  end
  
  def define_extension(substrategy, extension, options = {}, &block)
    substrategy = substrategy.to_sym
    extensions[ [substrategy, extension] ] = [block, options]
  end

  def get_substrategy_extension(substrategy, extension)
    key = [substrategy.to_sym, extension.to_sym]
    raise NameError.new("no extension #{extension} for substrategy #{substrategy} defined") unless extensions.include? key
    
    extensions[key]
  end
  
  class Template
    def apply(options)
      @applies.each do |mt|
        send mt, options
      end
    end
    
    def define(&block)
      @applies ||= []
      name = "apply_#{@applies.length}"
      class << self; self; end.class_eval do 
        private
        define_method name, &block
      end
      @applies << name
    end
    
    def instantiate(conf)
      result = self.clone
      result.instance_variable_set :@conf, conf
      result
    end
    
    def method_missing(name, *args, &block)
      @conf.send name, *args, &block
    end
  end
  
  def define_template(name, &block)
    (templates[name.to_sym] ||= Template.new).define(&block)
  end
  
  def apply_template(name, to_name, options = {})
    tmpl = templates[name.to_sym]
    if to_name.respond_to? :task
      # to_name is actually a particular configration
      to_name.instance_eval do
        s = self
        tmpl.instantiate(s).apply(options || {})
      end
    else
      base_templ = templates[:base]
      define_cfg to_name do
        s = self
        options = (options || {}).merge(:name => to_name.to_s)
        base_templ.instantiate(s).apply(options)
        tmpl.instantiate(s).apply(options)
      end
    end
  end
  
  class Area
    def define(&block)
      @applies ||= []
      @applies << block
    end
    
    def apply(configuration)
      @applies.each do |block|
        configuration.instance_eval(&block)
      end
    end
  end
  
  def define_area(name, &block)
    (areas[name.to_sym] ||= Area.new).define(&block)
  end
  
  def area_defined?(name)
    areas.key? name.to_sym
  end
  
  def apply_area(name, configuration)
    areas[name.to_sym].apply configuration
  end

  private
  def extensions
    @extensions ||= Hash.new
  end
  
  def name_filters
    @filters ||= Hash.new
  end
  
  def templates
    @templates ||= instance_eval do
      base_template = Template.new
      base_template.define { }
      { :base => base_template }
    end
  end
  
  def areas
    @areas ||= Hash.new
  end
  
  # does nothing when whole wondercap is used
  def compat_extension
  end
end
      
end
end